"""
日志工具模块
提供统一的日志记录功能，包括API请求日志、业务日志、错误日志等
"""
import os
import logging
import functools
import time
import traceback
from datetime import datetime
from typing import Any, Dict, Optional
from flask import request, g, current_app
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler


class FlinkLogger:
    """Flink监控系统日志管理器"""
    
    def __init__(self, name: str = None):
        self.logger_name = name or __name__
        self.logger = logging.getLogger(self.logger_name)
        
    @staticmethod
    def setup_app_logging(app):
        """为Flask应用配置日志系统"""
        log_level = os.environ.get("LOG_LEVEL", "INFO").upper()
        log_dir = os.path.join(os.path.dirname(__file__), "../../../logs")
        os.makedirs(log_dir, exist_ok=True)
        
        # 主应用日志
        app_log_path = os.path.join(log_dir, "backend.log")
        app_handler = RotatingFileHandler(
            app_log_path, 
            maxBytes=10 * 1024 * 1024,  # 10MB
            backupCount=5, 
            encoding="utf-8"
        )
        app_handler.setLevel(log_level)
        
        # API访问日志
        api_log_path = os.path.join(log_dir, "api_access.log")
        api_handler = TimedRotatingFileHandler(
            api_log_path,
            when='midnight',
            interval=1,
            backupCount=30,
            encoding='utf-8'
        )
        api_handler.setLevel(logging.INFO)
        
        # 错误日志
        error_log_path = os.path.join(log_dir, "error.log")
        error_handler = RotatingFileHandler(
            error_log_path,
            maxBytes=10 * 1024 * 1024,
            backupCount=10,
            encoding="utf-8"
        )
        error_handler.setLevel(logging.ERROR)
        
        # 日志格式
        detailed_formatter = logging.Formatter(
            "%(asctime)s [%(levelname)s] %(name)s - %(message)s "
            "(%(filename)s:%(lineno)d) [%(funcName)s]"
        )
        
        api_formatter = logging.Formatter(
            "%(asctime)s [API] %(message)s"
        )
        
        # 设置格式
        app_handler.setFormatter(detailed_formatter)
        api_handler.setFormatter(api_formatter)
        error_handler.setFormatter(detailed_formatter)
        
        # 清理现有handlers避免重复
        app.logger.handlers.clear()
        
        # 添加handlers
        app.logger.addHandler(app_handler)
        app.logger.setLevel(log_level)
        
        # 创建专用loggers
        api_logger = logging.getLogger('flink.api')
        api_logger.addHandler(api_handler)
        api_logger.setLevel(logging.INFO)
        api_logger.propagate = False
        
        error_logger = logging.getLogger('flink.error')
        error_logger.addHandler(error_handler)
        error_logger.setLevel(logging.ERROR)
        error_logger.propagate = False
        
        business_logger = app.logger
        
        return {
            'app': app.logger,
            'api': api_logger,
            'error': error_logger,
            'business': business_logger
        }
    
    @staticmethod
    def get_logger(logger_type: str = 'app') -> logging.Logger:
        """获取指定类型的logger"""
        logger_map = {
            'app': current_app.logger,
            'api': logging.getLogger('flink.api'),
            'error': logging.getLogger('flink.error'),
            'business': current_app.logger
        }
        return logger_map.get(logger_type, current_app.logger)
    
    @staticmethod
    def get_request_info() -> Dict[str, Any]:
        """获取请求信息"""
        try:
            return {
                'method': request.method,
                'url': request.url,
                'path': request.path,
                'remote_addr': request.remote_addr,
                'user_agent': request.headers.get('User-Agent', ''),
                'content_type': request.headers.get('Content-Type', ''),
                'content_length': request.headers.get('Content-Length', '0'),
                'args': dict(request.args),
                'form_keys': list(request.form.keys()) if request.form else [],
                'json_keys': list(request.json.keys()) if request.is_json and request.json else []
            }
        except Exception:
            return {}
    
    @staticmethod
    def log_api_request(endpoint: str, method: str, status_code: int, 
                       duration: float, error: str = None):
        """记录API请求日志"""
        api_logger = FlinkLogger.get_logger('api')
        request_info = FlinkLogger.get_request_info()
        
        log_data = {
            'endpoint': endpoint,
            'method': method,
            'status_code': status_code,
            'duration_ms': round(duration * 1000, 2),
            'remote_addr': request_info.get('remote_addr', ''),
            'user_agent': request_info.get('user_agent', '')[:100],  # 限制长度
            'args_count': len(request_info.get('args', {})),
            'form_keys_count': len(request_info.get('form_keys', [])),
            'json_keys_count': len(request_info.get('json_keys', []))
        }
        
        if error:
            log_data['error'] = error
        
        # 格式化日志消息
        log_message = (
            f"{method} {endpoint} | "
            f"Status: {status_code} | "
            f"Duration: {log_data['duration_ms']}ms | "
            f"IP: {log_data['remote_addr']} | "
            f"Args: {log_data['args_count']} | "
            f"Form: {log_data['form_keys_count']} | "
            f"JSON: {log_data['json_keys_count']}"
        )
        
        if error:
            log_message += f" | Error: {error}"
        
        if status_code >= 500:
            api_logger.error(log_message)
        elif status_code >= 400:
            api_logger.warning(log_message)
        else:
            api_logger.info(log_message)
    
    @staticmethod
    def log_business_operation(operation: str, details: Dict[str, Any] = None, 
                             success: bool = True, error: str = None):
        """记录业务操作日志"""
        business_logger = FlinkLogger.get_logger('business')
        
        log_data = {
            'operation': operation,
            'success': success,
            'timestamp': datetime.now().isoformat(),
            'details': details or {}
        }
        
        if error:
            log_data['error'] = error
        
        # 格式化日志消息
        status = "SUCCESS" if success else "FAILED"
        log_message = f"[{status}] {operation}"
        
        if details:
            detail_str = " | ".join([f"{k}: {v}" for k, v in details.items()])
            log_message += f" | {detail_str}"
        
        if error:
            log_message += f" | Error: {error}"
        
        if success:
            business_logger.info(log_message)
        else:
            business_logger.error(log_message)
    
    @staticmethod
    def log_error(error: Exception, context: str = None, extra_data: Dict[str, Any] = None):
        """记录错误日志"""
        error_logger = FlinkLogger.get_logger('error')
        
        error_data = {
            'error_type': type(error).__name__,
            'error_message': str(error),
            'traceback': traceback.format_exc(),
            'context': context or 'Unknown',
            'timestamp': datetime.now().isoformat(),
            'request_info': FlinkLogger.get_request_info()
        }
        
        if extra_data:
            error_data['extra_data'] = extra_data
        
        log_message = (
            f"[{error_data['error_type']}] {error_data['error_message']} | "
            f"Context: {error_data['context']} | "
            f"Path: {error_data['request_info'].get('path', 'N/A')}"
        )
        
        error_logger.error(log_message)
        error_logger.debug(f"Full traceback: {error_data['traceback']}")


def log_api_call(func_or_operation_name=None):
    """API调用日志装饰器
    支持两种使用方式:
    @log_api_call
    @log_api_call("operation_name")
    """
    def decorator(func):
        operation_name = func_or_operation_name if isinstance(func_or_operation_name, str) else None
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            operation = operation_name or f"{func.__module__}.{func.__name__}"
            
            # 记录请求开始
            app_logger = FlinkLogger.get_logger('app')
            request_info = FlinkLogger.get_request_info()
            
            app_logger.info(
                f"API Call Start: {operation} | "
                f"Method: {request_info.get('method', 'N/A')} | "
                f"Path: {request_info.get('path', 'N/A')} | "
                f"IP: {request_info.get('remote_addr', 'N/A')}"
            )
            
            try:
                # 执行原函数
                result = func(*args, **kwargs)
                duration = time.time() - start_time
                
                # 确定状态码
                status_code = 200
                if isinstance(result, tuple):
                    if len(result) >= 2 and isinstance(result[1], int):
                        status_code = result[1]
                
                # 记录成功日志
                FlinkLogger.log_api_request(
                    endpoint=request_info.get('path', 'N/A'),
                    method=request_info.get('method', 'N/A'),
                    status_code=status_code,
                    duration=duration
                )
                
                # 记录业务操作
                FlinkLogger.log_business_operation(
                    operation=operation,
                    details={
                        'duration_ms': round(duration * 1000, 2),
                        'status_code': status_code
                    },
                    success=status_code < 400
                )
                
                app_logger.info(
                    f"API Call Success: {operation} | "
                    f"Duration: {round(duration * 1000, 2)}ms | "
                    f"Status: {status_code}"
                )
                
                return result
                
            except Exception as e:
                duration = time.time() - start_time
                error_message = str(e)
                
                # 记录错误日志
                FlinkLogger.log_error(
                    error=e,
                    context=f"API Call: {operation}",
                    extra_data={
                        'duration_ms': round(duration * 1000, 2),
                        'function_args': str(args)[:200],  # 限制长度
                        'function_kwargs': str(kwargs)[:200]
                    }
                )
                
                # 记录API请求日志
                FlinkLogger.log_api_request(
                    endpoint=request_info.get('path', 'N/A'),
                    method=request_info.get('method', 'N/A'),
                    status_code=500,
                    duration=duration,
                    error=error_message
                )
                
                # 记录业务操作失败
                FlinkLogger.log_business_operation(
                    operation=operation,
                    details={
                        'duration_ms': round(duration * 1000, 2)
                    },
                    success=False,
                    error=error_message
                )
                
                app_logger.error(
                    f"API Call Failed: {operation} | "
                    f"Duration: {round(duration * 1000, 2)}ms | "
                    f"Error: {error_message}"
                )
                
                # 重新抛出异常
                raise
                
        return wrapper
    
    # 支持两种使用方式
    if callable(func_or_operation_name):
        # @log_api_call (无参数)
        return decorator(func_or_operation_name)
    else:
        # @log_api_call("name") (带参数)
        return decorator


def log_database_operation(func_or_operation_name=None):
    """数据库操作日志装饰器"""
    def decorator(func):
        operation_name = func_or_operation_name if isinstance(func_or_operation_name, str) else None
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            operation = operation_name or f"DB.{func.__name__}"
            
            app_logger = FlinkLogger.get_logger('app')
            app_logger.debug(f"Database Operation Start: {operation}")
            
            try:
                result = func(*args, **kwargs)
                duration = time.time() - start_time
                
                FlinkLogger.log_business_operation(
                    operation=operation,
                    details={
                        'duration_ms': round(duration * 1000, 2),
                        'type': 'database'
                    },
                    success=True
                )
                
                app_logger.debug(
                    f"Database Operation Success: {operation} | "
                    f"Duration: {round(duration * 1000, 2)}ms"
                )
                
                return result
                
            except Exception as e:
                duration = time.time() - start_time
                
                FlinkLogger.log_error(
                    error=e,
                    context=f"Database Operation: {operation}",
                    extra_data={'duration_ms': round(duration * 1000, 2)}
                )
                
                FlinkLogger.log_business_operation(
                    operation=operation,
                    details={
                        'duration_ms': round(duration * 1000, 2),
                        'type': 'database'
                    },
                    success=False,
                    error=str(e)
                )
                
                app_logger.error(
                    f"Database Operation Failed: {operation} | "
                    f"Duration: {round(duration * 1000, 2)}ms | "
                    f"Error: {str(e)}"
                )
                
                raise
                
        return wrapper
    
    # 支持两种使用方式
    if callable(func_or_operation_name):
        return decorator(func_or_operation_name)
    else:
        return decorator


def log_business_logic(operation_name: str, log_params: bool = True):
    """业务逻辑日志装饰器"""
    def business_logic_decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            operation = operation_name or f"Business.{func.__name__}"
            
            app_logger = FlinkLogger.get_logger('app')
            
            # 记录业务逻辑开始
            if log_params:
                app_logger.info(f"Business Logic Start: {operation}")
                app_logger.debug(f"Business Logic Parameters: {operation} | args={args}, kwargs={kwargs}")
            else:
                app_logger.info(f"Business Logic Start: {operation}")
            
            try:
                result = func(*args, **kwargs)
                duration = time.time() - start_time
                
                # 记录成功完成
                app_logger.info(f"Business Logic Success: {operation} | Duration: {round(duration * 1000, 2)}ms")
                
                # 记录业务操作日志
                FlinkLogger.log_business_operation(
                    operation=operation,
                    details={
                        'duration_ms': round(duration * 1000, 2),
                        'result_type': type(result).__name__ if result is not None else 'None',
                        'type': 'business_logic'
                    },
                    success=True
                )
                
                return result
                
            except Exception as e:
                duration = time.time() - start_time
                
                # 记录业务逻辑错误
                app_logger.error(f"Business Logic Failed: {operation} | Duration: {round(duration * 1000, 2)}ms | Error: {str(e)}")
                
                # 记录详细错误信息
                FlinkLogger.log_error(
                    error=e,
                    context=f"Business Logic: {operation}",
                    extra_data={
                        'duration_ms': round(duration * 1000, 2),
                        'function_args': args if log_params else 'hidden',
                        'function_kwargs': kwargs if log_params else 'hidden'
                    }
                )
                
                # 记录业务操作失败
                FlinkLogger.log_business_operation(
                    operation=operation,
                    details={
                        'duration_ms': round(duration * 1000, 2),
                        'error_type': type(e).__name__,
                        'type': 'business_logic'
                    },
                    success=False,
                    error=str(e)
                )
                
                raise
                
        return wrapper
    return business_logic_decorator


def log_flink_operation(operation_name: str):
    """Flink操作日志装饰器"""
    def flink_operation_decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            operation = operation_name or f"Flink.{func.__name__}"
            
            app_logger = FlinkLogger.get_logger('app')
            
            app_logger.info(f"Flink Operation Start: {operation}")
            
            try:
                result = func(*args, **kwargs)
                duration = time.time() - start_time
                
                app_logger.info(f"Flink Operation Success: {operation} | Duration: {round(duration * 1000, 2)}ms")
                
                FlinkLogger.log_business_operation(
                    operation=operation,
                    details={
                        'duration_ms': round(duration * 1000, 2),
                        'type': 'flink_operation'
                    },
                    success=True
                )
                
                return result
                
            except Exception as e:
                duration = time.time() - start_time
                
                app_logger.error(f"Flink Operation Failed: {operation} | Duration: {round(duration * 1000, 2)}ms | Error: {str(e)}")
                
                FlinkLogger.log_error(
                    error=e,
                    context=f"Flink Operation: {operation}",
                    extra_data={
                        'duration_ms': round(duration * 1000, 2)
                    }
                )
                
                FlinkLogger.log_business_operation(
                    operation=operation,
                    details={
                        'duration_ms': round(duration * 1000, 2),
                        'error_type': type(e).__name__,
                        'type': 'flink_operation'
                    },
                    success=False,
                    error=str(e)
                )
                
                raise
                
        return wrapper
    return flink_operation_decorator


def log_step(step_name: str, log_data: bool = False):
    """步骤日志装饰器，用于记录复杂业务流程中的各个步骤"""
    def step_decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            
            app_logger = FlinkLogger.get_logger('app')
            
            app_logger.debug(f"Step Start: {step_name}")
            
            try:
                result = func(*args, **kwargs)
                duration = time.time() - start_time
                
                if log_data and result is not None:
                    app_logger.debug(f"Step Success: {step_name} | Duration: {round(duration * 1000, 2)}ms | Result: {result}")
                else:
                    app_logger.debug(f"Step Success: {step_name} | Duration: {round(duration * 1000, 2)}ms")
                
                return result
                
            except Exception as e:
                duration = time.time() - start_time
                app_logger.warning(f"Step Failed: {step_name} | Duration: {round(duration * 1000, 2)}ms | Error: {str(e)}")
                raise
                
        return wrapper
    return step_decorator
